package com.yc.project2.org.springframework.context;

import com.yc.project2.org.springframework.annotation.*;

import java.io.File;
import java.io.FileFilter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zwei
 * @create 2024-08-01 15:24
 */
//spring基于注解配置的上下文类
public class YcAnnotationConfigApplicationContext implements YcApplicationContext {
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();
    private Map<String, YcBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    public YcAnnotationConfigApplicationContext(Class<?>... configClass) {
        for (Class cls : configClass) {
            //解析配置类中的@Bean注解
            parseBeanAnnotation(cls);
            //解析配置类中的@ComponentScan注解中的value  得到要扫描的包
            //待扫描的包的路径不为空  则递归扫描
            if (cls.isAnnotationPresent(YcComponentScan.class)) {
                YcComponentScan ycComponentScan = (YcComponentScan) cls.getAnnotation(YcComponentScan.class);
                //先看是否有value() 再看是否有basePackages() 如果都没有  则直接用配置类的路径做为待扫描路径
                String[] basePackages = ycComponentScan.value();
                if (basePackages == null || basePackages.length <= 0) {
                    basePackages = ycComponentScan.basePackages();
                }
                if (basePackages == null || basePackages.length <= 0) {
                    basePackages = new String[1];
                    //当前这个配置的类的全路径作为扫描路径
                    basePackages[0] = cls.getPackage().getName();
                }
                for (String s : basePackages) {
                    System.out.println("待扫描的包的路径：" + s);
                }
                //递归扫描这个配置类中  basePackages指定的包及子包下的所有的待托管的类
                recursiveLoadBeanDefinition(basePackages);
            }
        }
        try {
            //再ioc
            createBean();
            //再di
            doDi();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDi() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
        //循环BeanDefinitionMap  创建bean
        for (Map.Entry<String, Object> entry : this.beanMap.entrySet()) {
            //获取beanid
            String beanId = entry.getKey();
            //获取beanDefinition
            Object beanObject = entry.getValue();
            //查看这个对象的属性是否有di注解
            Field[] fields = beanObject.getClass().getDeclaredFields();
            for (Field f : fields) {
                if (f.isAnnotationPresent(YcResource.class)) {
                    //取出 YcResource注解中的value值 这个值是beanid  代表要装配的bean的id
                    String toBiBeanId = f.getAnnotation(YcResource.class).value();
                    Object obj = getToDiObject(toBiBeanId);
                    //设置  此field为可访问的
                    f.setAccessible(true);
                    f.set(beanObject, obj);  //this.userDao=userDao
                } else if (f.isAnnotationPresent(YcAutowired.class)) {
                    //TODO:  1.取出属性的类型  2.先从beanMap看是否有这种类型的bean  有则返回 没有则看beanDefinitionMap中是否这种类型
                }
            }
        }

    }

    private Object getToDiObject(String toDiBeanId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //如果beanMap中有  则直接返回
        if (beanMap.containsKey(toDiBeanId)) {
            return beanMap.get(toDiBeanId);
        }
        //判断beanDefinitionMap中是否有此BeanId  如果有 可以创建  如果没有 则报错
        if (this.beanDefinitionMap.containsKey(toDiBeanId) == false) {
            throw new RuntimeException("Expected at least one candidate bean but found none");
        }
        YcBeanDefinition bd = this.beanDefinitionMap.get(toDiBeanId);
        if (bd.isLazy()) {
            String classpath = bd.getClassInfo();
            Object beanObj = Class.forName(classpath).newInstance();
            beanMap.put(toDiBeanId, beanObj);
            return beanObj;
        }
        if (bd.getScope().equalsIgnoreCase("prototype")) {
            String classpath = bd.getClassInfo();
            Object beanobj = Class.forName(classpath).newInstance();
            //因为prototype模式下 bean每次都要重新创建  所以没有存到beanMap中
            return beanobj;
        }
        return null;
    }

    /**
     * IOC
     */
    private void createBean() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //循环BeanDefinitionMap 创建bean
        for (Map.Entry<String, YcBeanDefinition> entry : this.beanDefinitionMap.entrySet()) {
            //获取beanid
            String beanId = entry.getKey();
            //获取beanDefinition
            YcBeanDefinition bd = entry.getValue();
            //判断beanDefinition中是否有lazy属性
            if (!bd.isLazy() && !bd.getScope().equalsIgnoreCase("prototype")) {
                //创建bean
                String classInfo = bd.getClassInfo();
                //创建bean的方式有多种 这里只用到了无参构造方法创建的方式
                Object beanobj = Class.forName(classInfo).newInstance();
                beanMap.put(beanId, beanobj);
            }
        }
    }

    /**
     * 递归 扫描 baskPackages中的路径  加载所有的托管类信息到beanDefinitionMap中
     *
     * @param basePackages={"com.yc","com.yc.project2"}
     */
    private void recursiveLoadBeanDefinition(String[] basePackages) {
        //循环此数组
        for (String basePackage : basePackages) {
            //加载包下.class文件  -》流 -》文件路径-》com/yc/project2/org/
            //替换包路径中的.  /
            String packagePath = basePackage.replaceAll("\\.", "/");
            try {
                //利用jvm加载器加载包下的所有.class文件
                //线程上下文类加载器
                Enumeration<URL> files = Thread.currentThread().getContextClassLoader().getResources(packagePath);
                while (files.hasMoreElements()) {
                    URL url = files.nextElement();
                    System.out.println("正在扫描的包下的文件路径为：" + url.getFile()); //多了一个 开头/
                    //获取这个包下的所有的文件   筛选出 .class的文件
                    findPackageClasses(url.getFile(), basePackage);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param packagePath 待扫描的包的绝对路径 ： /c:/xxx/xxx/com/yc/
     * @param packageName com.yc  包路径： com.yc
     */
    private void findPackageClasses(String packagePath, String packageName) {
        if (packagePath.startsWith("/")) {
            //去掉开头的/
            packagePath = packagePath.substring(1);
        }
        try {
            packagePath= URLDecoder.decode(packagePath,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        //递归扫描
        File file = new File(packagePath);
        //获取所有的 .class文件  或者目录
        File[] classFiles = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".class") || pathname.isDirectory()) {
                    return true;
                } else {
                    return false;
                }
            }
        });
        //System.out.println(classFiles);
        //循环 classFile  如果是.class文件则加载  如果是目录则递归扫描
        if (classFiles != null && classFiles.length > 0) {
            for (File cf : classFiles) {
                if (cf.isDirectory()) {  //如果是目录递归扫描
                    findPackageClasses(cf.getAbsolutePath(), packageName + "." + cf.getName());
                } else {
                    //是.class文件 则jvm加载
                    URLClassLoader uc = new URLClassLoader(new URL[]{});
                    try {
                        Class cls = uc.loadClass(packageName + "." + cf.getName().replace(".class", ""));

                        //判断这个cls类上是否有IOC注解  如果有 则创建一个BeanDefinition对象 存到beanDefinitionMap中
                        if (cls.isAnnotationPresent(YcComponent.class) ||
                                cls.isAnnotationPresent(YcRepository.class) ||
                                cls.isAnnotationPresent(YcService.class) ||
                                cls.isAnnotationPresent(YcController.class)
                        ) {
                            YcBeanDefinition bd = new YcBeanDefinition();
                            if (cls.isAnnotationPresent(YcLazy.class)) {
                                YcLazy lazy = (YcLazy) cls.getAnnotation(YcLazy.class);
                                boolean b = lazy.value();
                                bd.setLazy(b);
                            }
                            if (cls.isAnnotationPresent(YcScope.class)) {
                                YcScope scope = (YcScope) cls.getAnnotation(YcScope.class);
                                String s = scope.value();
                                bd.setScope(s);
                            }
                            bd.setClassInfo(packageName + "." + cf.getName().replace(".class", ""));
                            //获取beanid
                            String beanId = getBeanId(cls);
                            this.beanDefinitionMap.put(beanId, bd);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private String getBeanId(Class cls) {
        String beanId = "";
        YcComponent ycComponent = (YcComponent) cls.getAnnotation(YcComponent.class);
        YcRepository ycRepository = (YcRepository) cls.getAnnotation(YcRepository.class);
        YcService ycService = (YcService) cls.getAnnotation(YcService.class);
        YcController ycController = (YcController) cls.getAnnotation(YcController.class);
        if (ycComponent != null) {
            beanId = ycComponent.value();
        } else if (ycRepository != null) {
            beanId = ycRepository.value();
        } else if (ycService != null) {
            beanId = ycService.value();
        } else if (ycController != null) {
            beanId = ycController.value();
        }
        //如果按value()取不到beanid名 则按类名
        if (beanId == null || "".equalsIgnoreCase(beanId)) {
            String typename = cls.getSimpleName();
            beanId = typename.substring(0, 1).toLowerCase() + typename.substring(1);
        }
        return beanId;
    }

    /**
     * 在cls配置类中查找@Bean注解修饰的方法  托管其对应的类的对象
     *
     * @param cls
     */
    private void parseBeanAnnotation(Class cls) {
        try {
            //将配置类交给spring托管
            Object obj = cls.newInstance();
            String claName = cls.getSimpleName();
            YcBeanDefinition bd = new YcBeanDefinition();
            bd.setLazy(false);
            bd.setScope("singleton");
            bd.setClassInfo(cls.getName());
            String beanId = claName.substring(0, 1).toLowerCase() + claName.substring(1);
            beanDefinitionMap.put(beanId, bd);
            beanMap.put(beanId, obj);

            //在这个配置类cls中查找所有@YcBean注解 的方法  解析它们  托管对应的类的对象
            Method[] ms = cls.getDeclaredMethods();
            //只取有@YcBean注解的方法
            for (Method m : ms) {
                if (m.isAnnotationPresent(YcBean.class)) {
                    YcBean ycBean = m.getAnnotation(YcBean.class);
                    String ycBeanId = ycBean.value();
                    if (ycBeanId.equals("")) {
                        ycBeanId = m.getName();
                    }
                    //激活方法
                    Object o = m.invoke(obj, null);
                    this.beanMap.put(ycBeanId, o);
                    YcBeanDefinition bd1 = new YcBeanDefinition();
                    bd1.setLazy(false);
                    bd1.setScope("singleton");
                    bd1.setClassInfo(o.getClass().getName());   //Apple类的全路径
                    this.beanDefinitionMap.put(ycBeanId, bd1);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanId) {
        YcBeanDefinition bd = this.beanDefinitionMap.get(beanId);
        if (bd == null) {
            throw new RuntimeException("查无此bean");
        }
        String scope = bd.getScope();
        if ("prototype".equalsIgnoreCase(scope)) {
            Object obj = null;
            try {
                obj = Class.forName(bd.getClassInfo()).newInstance();
                this.beanMap.put(beanId, obj);
                doDi();
                this.beanMap.remove(beanId);
                return obj;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        if (beanMap.containsKey(beanId)) {
            return beanMap.get(beanId);
        }
        if (bd.isLazy()) {
            Object obj = null;
            try {
                obj = Class.forName(bd.getClassInfo()).newInstance();
                beanMap.put(beanId, obj);
                doDi();
                return obj;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }
}
